package com.xm.web.aop;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xm.common.entity.R;
import com.xm.web.entity.dto.OperateLogDTO;
import com.xm.web.utils.IpUtil;
import com.xm.web.utils.SecurityUtil;
import com.xm.web.utils.ThreadPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Map;
import java.util.TreeMap;

/**
 * 日志埋点
 *
 * @author yousj
 * @since 2022-03-22
 */
@Slf4j
@Aspect
@Order(1)
@Component
public class RequestLogAop {

    @Pointcut("execution(* com.xm.web.controller..*.*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        OperateLogDTO operateLogDTO = new OperateLogDTO();
        ServletRequestAttributes attributes = null;
        try {
            attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            // 不记录非web请求
            if (attributes != null) {
                Date startRequestTime = new Date();
                operateLogDTO.setGmtCreate(startRequestTime)
                        .setGmtModified(startRequestTime)
                        .setStartRequestTime(startRequestTime);
                HttpServletRequest request = attributes.getRequest();
                String beanName = joinPoint.getSignature().getDeclaringTypeName();
                String methodName = joinPoint.getSignature().getName();
                String uri = request.getRequestURI();
                String serverName = request.getServerName();
                String requestMethod = request.getMethod();
                Map<String, Object> map = new TreeMap<>();
                for (Object arg : joinPoint.getArgs()) {
                    if (arg == null
                            || MultipartFile.class.isAssignableFrom(arg.getClass())
                            || MultipartFile[].class.isAssignableFrom(arg.getClass())
                            || ServletRequest.class.isAssignableFrom(arg.getClass())
                            || ServletResponse.class.isAssignableFrom(arg.getClass())) {
                        continue;
                    }
                    String jsonString = JSON.toJSONString(arg);
                    if (JSON.isValidObject(jsonString)) {
                        JSONObject jsonObject = JSON.parseObject(jsonString);
                        map.putAll(jsonObject);
                    }
                }
                map.putAll(request.getParameterMap());
                String requestParams = JSON.toJSONString(map);
                operateLogDTO
                        .setResource(uri)
                        .setBeanName(beanName)
                        .setMethodName(methodName)
                        .setRemoteIpAddr(IpUtil.getIpAddr(request))
                        .setRemoteUserAgent(request.getHeader(HttpHeaders.USER_AGENT))
                        .setRequestMethod(requestMethod)
                        .setRequestParams(requestParams)
                        .setServerName(serverName);
            }
        } catch (Exception e) {
            log.error("埋点日志记录异常", e);
        }

        R<?> r = null;
        Object result;
        try {
            result = joinPoint.proceed();
            if (result != null && result.getClass() == R.class) {
                r = (R<?>) result;
            }
            return result;
        } finally {
            try {
                if (attributes != null) {
                    if (r != null) {
                        try {
                            operateLogDTO.setUserId(SecurityUtil.getCurrentUserId());
                        } catch (Exception e) {
                            //Ignore
                        }
                        operateLogDTO.setResponseCode(r.getCode())
                                .setResponseMessage(r.getMessage());
//                        if (r.getData() != null) {
//                            String responseData = JSON.toJSONString(r.getData());
//                            operateLogDTO.setResponseData(responseData.length() < 2000 ? JSON.toJSONString(responseData) : null);
//                        }
                    }
                    operateLogDTO.setResponseTiming(ChronoUnit.MILLIS.between(operateLogDTO.getStartRequestTime().toInstant(), Instant.now()));
                    ThreadPoolUtil.LOG_EXECUTOR_POOL.execute(() -> {
                        // TODO 日志埋点, 日志表/消息队列
                        log.info(JSON.toJSONString(operateLogDTO));
                    });
                }
            } catch (Exception e) {
                //Ignore
            }
        }
    }

}